#include <linux/printk.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/of.h>
#include <asm/io.h>

#include "chip_demo_gpio_led.h"
#include "led_drv.h"


static struct GPIOx *gpio1, *gpio5;
volatile unsigned int *GPIO1_CLK, *GPIO5_CLK;
volatile unsigned int *IOMUXC_GPIO1, *IOMUXC_GPIO5;

int led_gpio_pins[10];  //保存引脚
int led_cnts;    		//记录引脚数量


/*
	接收 led_drv.c 传递的初始化引脚参数（接收led_drv.c传递的次设备号）
	负责初始化GPIO
*/
static int led_chip_init(int which)
{
	int gro, p;
	
	gro = GROUP(led_gpio_pins[which]);   //所属GPIO组
	p = pin(led_gpio_pins[which]);		 //第几个引脚
	
	printk("%s: control led of %d\n", __FUNCTION__, which);
	printk("init group %d, pin %d\n", gro, p);

	/* 初始化具体GPIO */
	switch(gro){
		case 1:
			if(!gpio1)
			{
				/* 映射CLK寄存器 */
				GPIO1_CLK = ioremap(CCM_CCGR1, 4);
				
				/* 映射GPIOx寄存器，包含数据、输出方向寄存器 */
				gpio1 = ioremap(GPIO1_DR_ADDR, sizeof(struct GPIOx));

				/* 映射IOMUXC寄存器，设置成GPIO外设 */
				IOMUXC_GPIO1 = ioremap(IOMUXC_GPIO1_ADDR,4);

				/* 设置成GPIO外设 */
				*IOMUXC_GPIO1 &= 0x0f;
				*IOMUXC_GPIO1 |= 0x5;

				/* 使能时钟 */
				*GPIO1_CLK |= (3 << 26);

				/* 设置GPIO方向：输出 */
				gpio1->GDIR |= (1 << p);
			}
			
		case 5:
			if(!gpio5)
			{
				/* 映射CLK寄存器 */
				GPIO5_CLK = ioremap(CCM_CCGR1, 4);
				
				/* 映射GPIOx寄存器，包含数据、输出方向寄存器 */
				gpio5 = ioremap(GPIO5_DR_ADDR, sizeof(struct GPIOx));
				
				/* 映射IOMUXC寄存器，设置成GPIO外设 */
				IOMUXC_GPIO5 = ioremap(IOMUXC_GPIO5_ADDR,4);

				/* 设置成GPIO外设 */
				*IOMUXC_GPIO5 &= 0x0f;
				*IOMUXC_GPIO5 |= 0x5;
				
				/* 使能时钟 */
				*GPIO5_CLK |= (3 << 30);

				/* 设置GPIO方向：输出 */
				gpio5->GDIR |= (1 << p);
			}
		default:
			break;
	}
	
	return 0;
}


/*
	接收 led_drv.c 传递的控制引脚参数
	负责控制GPIO输出高低电平
*/
static int led_chip_ctl(int which, char status)
{
	int gro, p;
	
	gro = GROUP(led_gpio_pins[which]);   //所属GPIO组
	p = pin(led_gpio_pins[which]);		 //第几个引脚
	
	printk("%s: control led of %d, status = %d\n", __FUNCTION__, which, status);
	printk("control group %d, pin %d\n", gro, p);

	/* 控制具体GPIO */
	switch(gro){
		case 1:
			gpio1 = ioremap(GPIO1_DR_ADDR, sizeof(struct GPIOx));
			if(status)
				gpio1->DR |= (1<<p);
			else
				gpio1->DR &= ~(1<<p);
			break;
			
		case 2:
			break;
			
		case 3:
			break;
			
		case 4:
			break;
			
		case 5:
			gpio5 = ioremap(GPIO5_DR_ADDR, sizeof(struct GPIOx));
			if(status)
				gpio5->DR |= (1<<p);
			else
				gpio5->DR &= ~(1<<p);
			break;
			
		default:
			break;
	}
	
	return 0;
}

	
/*
	led_chip_operations 结构体变量
	GPIO的初始化和控制函数指针
*/
static struct led_chip_operations led_chip_ops = {
	.led_init = led_chip_init,
	.led_ctl  = led_chip_ctl,
};

static int chip_led_probe(struct platform_device *dev)
{
	/* 从设备树节点中提取出引脚 */
	struct device_node *np = dev->dev.of_node;
	int led_pin, err;

	/* 定义二级指针 */
	char name[20]={0};
	const char *str;
	str = name;

	/* 此platform_driver支持的platform_device可能不来自设备树
	这里要判断一下*/
	if(!np)
		return -1;
	
	/* 从设备树节点中取出 my_name 属性 */
	err = of_property_read_string(np, "my_name", &str);
	/* 从设备树节点中取出pin属性 */
	err = of_property_read_u32(np, "pin", &led_pin);

	printk("chip_led_probe fun, name=%s, group=%d, pin=%d\n", str,GROUP(led_pin),pin(led_pin));

	led_gpio_pins[led_cnts] = led_pin;
	/* 创建设备节点 */
	led_device_create(led_cnts);
	led_cnts++;
	
	return 0;
}

static int chip_led_remove(struct platform_device *dev)
{
	int i;
	int led_pin, err;
	int flag = 0;  //判断是否所有设备节点都被销毁

	/* 定义二级指针 */
	char name[20]={0};
	const char *str;
	
	/* 从设备树节点中提取出引脚 */
	struct device_node *np = dev->dev.of_node;
	/* 此platform_driver支持的platform_device可能不来自设备树
	这里要判断一下*/
	if(!np)
		return -1;
	/* 从设备树节点中取出 pin 属性 */
	err = of_property_read_u32(np, "pin", &led_pin);

	/* 从设备树节点中取出 my_name 属性 */
	str = name;
	err = of_property_read_string(np, "my_name", &str);
	
	printk("chip_led_remove fun, name=%s, group=%d, pin=%d\n", str,GROUP(led_pin),pin(led_pin));
	
	/* 卸载设备节点 */
	for(i=0; i<led_cnts; i++)
	{
		if(led_gpio_pins[i] != -1)
			flag = 1;
		if(led_gpio_pins[i] == led_pin)
		{
			led_device_destroy(i);
			led_gpio_pins[i] = -1;
		}
	}
	if(flag == 0)
		led_cnts = 0;
	
	return 0;
}


static const struct of_device_id led_match[] = {
	{ .compatible = "hc-led-beep" },
	{ .compatible = "hc-led-beep" },
	{},
};


static struct platform_driver chip_led_platform_drv = {
	.probe		= chip_led_probe,
	.remove		= chip_led_remove,
	.driver		= {
		.name	= "led-beep",
		.of_match_table = led_match,
	},
};

static int chip_led_platform_init(void)
{
	int err;
	err = platform_driver_register(&chip_led_platform_drv);
	get_led_resources(&led_chip_ops);

	return 0;
}

static void chip_led_platform_exit(void)
{
	platform_driver_unregister(&chip_led_platform_drv);
}

module_init(chip_led_platform_init);
module_exit(chip_led_platform_exit);
MODULE_LICENSE("GPL");



